package com.tcm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.common.enums.CachePrefix;
import com.tcm.entity.*;
import com.tcm.entity.bo.UserCacheBo;
import com.tcm.entity.dto.SocialClazzUpdateDto;
import com.tcm.entity.dto.param.SocialClazzPageParam;
import com.tcm.entity.dto.param.SocialClazzSetTeacherParam;
import com.tcm.entity.resp.PageR;
import com.tcm.entity.vo.TeacherListResult;
import com.tcm.exception.DatabaseOperationException;
import com.tcm.exception.EntityInvalidException;
import com.tcm.mapper.SocialClazzMapper;
import com.tcm.mapper.SocialClazzTeacherMapper;
import com.tcm.service.*;
import com.tcm.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 社会化班级业务实现类
 * @author Guqier
 * @version 1.0
 * @date 2023/7/23 19:41
 */
@Slf4j
@Service
public class SocialClazzServiceImpl extends ServiceImpl<SocialClazzMapper, SocialClazz> implements SocialClazzService {

    @Autowired
    private SocialProjectService socialProjectService;

    @Autowired
    private TeacherService teacherService;

    @Autowired
    private SocialStudentService socialStudentService;

    @Autowired
    private UserService userService;

    @Autowired
    private SocialClazzMapper socialClazzMapper;

    @Autowired
    private SocialClazzTeacherMapper socialClazzTeacherMapper;

    public static final Integer SOCIAL_CLAZZ_TEACHER_TYPE = 1;// 社会化班级教师关系，授课教师类型

    public static final Integer SOCIAL_CLAZZ_PRESIDENT_TEACHER_TYPE = 0;// 社会化班级教师关系，班主任

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 添加社会化班级
     * @param dto 社会化班级数据更新封装类
     * @param currUserId 当前登录用户ID
     * @return 处理结果，true为成功
     */
    @Override
    @Transactional
    public boolean addSocialClazz(SocialClazzUpdateDto dto, Long currUserId) throws EntityInvalidException{
        // 校验社会化项目是否存在
        SocialProject project = socialProjectService.getById(dto.getProjectId());
        if (project == null) {
            throw new EntityInvalidException("社会化项目不存在");
        }
        // 校验教师是否存在
        Teacher teacher = teacherService.getById(dto.getTeacherId());
        if (teacher == null) {
            throw new EntityInvalidException("教师不存在");
        }
        // 创建实体类
        SocialClazz socialClazz = new SocialClazz();
        BeanUtils.copyProperties(dto, socialClazz);
        socialClazz.setCreateUser(currUserId);
        socialClazz.setUpdateUser(currUserId);
        boolean saved = this.save(socialClazz);
        // 保存班主任关系
        SocialClazzTeacher socialClazzTeacher = new SocialClazzTeacher();
        socialClazzTeacher.setSocialClazzId(socialClazz.getId());
        socialClazzTeacher.setTeacherId(teacher.getId());
        socialClazzTeacher.setType(SOCIAL_CLAZZ_PRESIDENT_TEACHER_TYPE);// 类型为班主任
        socialClazzTeacher.setCreateUser(currUserId);
        socialClazzTeacher.setUpdateUser(currUserId);
        boolean save = socialClazzTeacherMapper.insert(socialClazzTeacher) == 1;
        // 保存授课教师
        boolean b = true;
        List<Long> teacherIds = dto.getTeacherIds();
        if (!CollectionUtils.isEmpty(teacherIds)) {
            SocialClazzSetTeacherParam socialClazzSetTeacherParam = new SocialClazzSetTeacherParam();
            socialClazzSetTeacherParam.setSocialClazzId(socialClazz.getId());
            socialClazzSetTeacherParam.setTeacherIds(teacherIds);
            b = setSocialClazzTeacher(socialClazzSetTeacherParam, currUserId);
        }
        if (!save || !saved || !b){
            throw new DatabaseOperationException("数据库操作失败");
        }
        return true;
    }

    /**
     * 修改社会化班级信息
     * @param dto 社会化班级数据更新封装类
     * @param currUserId 当前登录用户ID
     * @return 处理结果，true为成功
     */
    @Override
    @Transactional
    public boolean modifySocialClazz(SocialClazzUpdateDto dto, Long currUserId) throws EntityInvalidException{
        // 校验实体是否存在
        SocialClazz socialClazz = this.getById(dto.getId());
        if (socialClazz == null) {
            throw new EntityInvalidException("社会化班级不存在");
        }
        // 校验教师是否存在
        Teacher teacher = teacherService.getById(dto.getTeacherId());
        if (teacher == null) {
            throw new EntityInvalidException("教师不存在");
        }
        // 校验社会化项目是否存在
        if (!dto.getProjectId().equals(socialClazz.getProjectId())){
            SocialProject project = socialProjectService.getById(dto.getProjectId());
            if (project == null) {
                throw new EntityInvalidException("社会化项目不存在");
            }
        }
        // 修改班主任关系
        if (!teacher.getId().equals(socialClazz.getTeacherId())){
            // 查找原有班主任记录
            LambdaQueryWrapper<SocialClazzTeacher> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SocialClazzTeacher::getSocialClazzId, socialClazz.getId())
                    .eq(SocialClazzTeacher::getTeacherId, socialClazz.getTeacherId())
                    .eq(SocialClazzTeacher::getType, SOCIAL_CLAZZ_PRESIDENT_TEACHER_TYPE);
            SocialClazzTeacher socialClazzTeacher = socialClazzTeacherMapper.selectOne(queryWrapper);
            boolean b = true;// 标记变量
            if (socialClazzTeacher == null){
                // 保存班主任关系
                socialClazzTeacher = new SocialClazzTeacher();
                socialClazzTeacher.setSocialClazzId(socialClazz.getId());
                socialClazzTeacher.setTeacherId(teacher.getId());
                socialClazzTeacher.setType(SOCIAL_CLAZZ_PRESIDENT_TEACHER_TYPE);// 类型为班主任
                socialClazzTeacher.setCreateUser(currUserId);
                socialClazzTeacher.setUpdateUser(currUserId);
                b = socialClazzTeacherMapper.insert(socialClazzTeacher) == 1;
            }else {
                socialClazzTeacher.setTeacherId(teacher.getId());
                socialClazzTeacher.setUpdateUser(currUserId);
                b = socialClazzTeacherMapper.updateById(socialClazzTeacher) == 1;
            }
            if (!b){
                throw new DatabaseOperationException("修改班主任关系失败");
            }
        }
        // 保存实体类
        BeanUtils.copyProperties(dto, socialClazz);
        socialClazz.setUpdateUser(currUserId);
        boolean bClazz = this.updateById(socialClazz);
        // 同步班级下社会化学员账号有效期
        LambdaQueryWrapper<SocialStudent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SocialStudent::getClazzId, socialClazz.getId());
        List<SocialStudent> socialStudents = socialStudentService.list(queryWrapper);
        int cnt = 0;
        if (!CollectionUtils.isEmpty(socialStudents)) {
            for (SocialStudent socialStudent : socialStudents) {
                User user = userService.getUserByAccount(socialStudent.getAccount());
                user.setDisableTime(socialClazz.getInvalidTime());
                user.setUpdateUser(currUserId);
                if (userService.updateById(user)) {
                    cnt ++;
                }
                redisUtil.deleteObject(CachePrefix.PC_USER_INFO.getCacheKey(user.getAccount()));
            }
        }
        // 保存授课教师
        boolean b = true;
        List<Long> teacherIds = dto.getTeacherIds();
        if (!CollectionUtils.isEmpty(teacherIds)) {
            SocialClazzSetTeacherParam socialClazzSetTeacherParam = new SocialClazzSetTeacherParam();
            socialClazzSetTeacherParam.setSocialClazzId(socialClazz.getId());
            socialClazzSetTeacherParam.setTeacherIds(teacherIds);
            b = setSocialClazzTeacher(socialClazzSetTeacherParam, currUserId);
        }
        if (!bClazz || cnt != socialStudents.size() || !b){
            throw new DatabaseOperationException("数据修改失败");
        }
        return true;
    }

    /**
     * 批量删除社会化班级
     * @param ids 社会化班级列表
     * @param currUserId 当前登录用户ID
     * @return 处理结果，true为成功
     */
    @Override
    @Transactional
    public boolean removeSocialClazz(List<Long> ids, Long currUserId){
        if (CollectionUtils.isEmpty(ids)) {
            return false;
        }
        int cnt = 0;
        for (Long id : ids) {
            // 删除社会化班级实体
            SocialClazz socialClazz = this.getById(id);
            if (socialClazz == null){
                break;
            }
            socialClazz.setUpdateUser(currUserId);
            boolean b = this.updateById(socialClazz);
            boolean b1 = this.removeById(id);

            // 删除班级/教师关系
            LambdaQueryWrapper<SocialClazzTeacher> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(SocialClazzTeacher::getSocialClazzId, socialClazz.getId());
            Integer dCount = socialClazzTeacherMapper.selectCount(queryWrapper);
            int d1 = socialClazzTeacherMapper.delete(queryWrapper);
            if (b && b1 && dCount.equals(d1)){
                cnt++;
            }
        }
        if (cnt != ids.size()){
            throw new DatabaseOperationException("删除失败");
        }
        return true;
    }

    /**
     * 分页获取社会化班级列表
     * @param param 分页参数封装
     * @return 社会化班级列表
     */
    @Override
    public PageR<SocialClazzUpdateDto> getSocialClazzListByPage(SocialClazzPageParam param){
        // 构造条件
        Page<SocialClazz> page = new Page<>(param.getPage(), param.getPageSize());
        page.addOrder(OrderItem.desc("update_time"));
        LambdaQueryWrapper<SocialClazz> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StringUtils.hasText(param.getNum()), SocialClazz::getNum, param.getNum());
        queryWrapper.eq(StringUtils.hasText(param.getName()), SocialClazz::getName, param.getName());
        queryWrapper.eq(param.getProjectId() != null, SocialClazz::getProjectId, param.getProjectId());
        // 查询结果
        this.page(page, queryWrapper);
        // 构造结果集
        PageR<SocialClazzUpdateDto> pageR = new PageR<>();
        BeanUtils.copyProperties(page, pageR, "records");
        pageR.setRecords(transferSocialClazzToDto(page.getRecords()));
        return pageR;
    }

    /**
     * 通过社会化项目ID获取社会化班级列表
     * @param projectId 社会化项目ID
     * @return 社会化班级列表
     * @throws EntityInvalidException 实体不可用异常
     */
    @Override
    public List<SocialClazzUpdateDto> getSocialClazzListByProjectId(Long projectId) throws EntityInvalidException{
        // 校验项目是否存在
        SocialProject project = socialProjectService.getById(projectId);
        if (project == null) {
            throw new EntityInvalidException("社会化项目不存在");
        }
        // 构造条件
        LambdaQueryWrapper<SocialClazz> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SocialClazz::getProjectId, project.getId());
        return this.list(queryWrapper).stream().map(socialClazz -> {
            SocialClazzUpdateDto dto = new SocialClazzUpdateDto();
            BeanUtils.copyProperties(socialClazz, dto);
            return dto;
        }).collect(Collectors.toList());
    }

    /**
     * 屏蔽逻辑删除获取社会化班级列表
     * @return 社会化班级列表
     */
    @Override
    public List<SocialClazz> getSocialClazzListWithoutLogicDelete(){
        return socialClazzMapper.selectAllWithoutLogicDelete();
    }

    /**
     * 设置社会化班级授课教师
     * @param param 参数封装
     * @param currUserId 当前登录用户ID
     * @return 处理结果，true为成功
     */
    @Override
    @Transactional
    public boolean setSocialClazzTeacher(SocialClazzSetTeacherParam param, Long currUserId){
        // 校验社会化班级是否存在
        SocialClazz socialClazz = this.getById(param.getSocialClazzId());
        if (socialClazz == null) {
            throw new EntityInvalidException("班级不存在");
        }
        // 校验教师是否存在
        List<Long> teacherIds = param.getTeacherIds();
        List<Teacher> teachers = teacherService.getBaseMapper().selectBatchIds(teacherIds);
        if (teacherIds.size() != teachers.size()){
            throw new EntityInvalidException("教师不存在");
        }
        // 删除原有教师关系
        LambdaQueryWrapper<SocialClazzTeacher> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SocialClazzTeacher::getSocialClazzId, socialClazz.getId());
        Integer dCount = socialClazzTeacherMapper.selectCount(queryWrapper);
        int deleted = socialClazzTeacherMapper.delete(queryWrapper);
        if (dCount != deleted){
            throw new DatabaseOperationException("原有教师关系删除失败");
        }
        // 添加新教师关系
        int cnt = 0;// 计数器
        for (Teacher teacher : teachers) {
            SocialClazzTeacher socialClazzTeacher = new SocialClazzTeacher();
            socialClazzTeacher.setSocialClazzId(socialClazz.getId());
            socialClazzTeacher.setTeacherId(teacher.getId());
            socialClazzTeacher.setType(SOCIAL_CLAZZ_TEACHER_TYPE);
            socialClazzTeacher.setCreateUser(currUserId);
            socialClazzTeacher.setUpdateUser(currUserId);
            if (socialClazzTeacherMapper.insert(socialClazzTeacher) == 1) {
                cnt ++;
            }
        }
        if (cnt != teachers.size()){
            throw new DatabaseOperationException("添加新教师关系失败");
        }
        return true;
    }

    /**
     * 转换社会化班级实体类为Dto
     * @param socialClazzList 社会化班级实体类列表
     * @return Dto
     */
    @Override
    public List<SocialClazzUpdateDto> transferSocialClazzToDto(List<SocialClazz> socialClazzList){
        // 准备数据
        List<SocialProject> socialProjects = socialProjectService.list();
        List<Teacher> teacherList = teacherService.list();
        List<SocialClazzTeacher> socialClazzTeachers = socialClazzTeacherMapper.selectList(null);
        // 构造匹配数据
        return socialClazzList.stream().map(socialClazz -> {
            SocialClazzUpdateDto dto = new SocialClazzUpdateDto();
            BeanUtils.copyProperties(socialClazz, dto);
            // 查找班主任
            teacherList
                    .stream()
                    .filter(teacher -> teacher.getId().equals(socialClazz.getTeacherId()))
                    .findFirst()
                    .ifPresent(teacher -> dto.setTeacherName(teacher.getName()));
            // 查找社会化项目名
            socialProjects
                    .stream()
                    .filter(socialProject -> socialProject.getId().equals(socialClazz.getProjectId()))
                    .findFirst()
                    .ifPresent(socialProject -> dto.setProjectName(socialProject.getName()));
            // 查找授课教师
            List<Long> teacherIds = socialClazzTeachers
                    .stream()
                    .filter(socialClazzTeacher -> socialClazzTeacher.getSocialClazzId().equals(socialClazz.getId()))
                    .map(SocialClazzTeacher::getTeacherId)
                    .collect(Collectors.toList());
            List<TeacherListResult> teacherListResults = teacherService.transferTeacherToListResult(teacherList.stream().filter(teacher -> teacherIds.contains(teacher.getId())).collect(Collectors.toList()));
            dto.setTeachers(teacherListResults);
            return dto;
        }).collect(Collectors.toList());
    }

    /**
     * 根据当前登录教师获得社会化班级列表
     * @param currUserId 当前登录用户ID
     * @return 社会化班级列表
     */
    @Override
    public List<SocialClazzUpdateDto> getSocialClazzListAccordingToTeacher(Long currUserId){
        // 从缓存中获取当前登录用户信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(String.valueOf(currUserId)));
        Teacher teacher = teacherService.getTeacherByAccount(userCacheBo.getAccount());
        if (teacher == null) {
            return null;// 如果当前登录用户是管理员而不是教师
        }
        // 查询指定社会化班级
        LambdaQueryWrapper<SocialClazzTeacher> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SocialClazzTeacher::getTeacherId, teacher.getId());
        List<Long> socialClazzIds = socialClazzTeacherMapper.selectList(queryWrapper).stream().map(SocialClazzTeacher::getSocialClazzId).collect(Collectors.toList());
        // 匹配数据
        List<SocialClazz> socialClazzList = this.list();
        return transferSocialClazzToDto(socialClazzList.stream().filter(socialClazz -> socialClazzIds.contains(socialClazz.getId())).collect(Collectors.toList()));
    }
}
